#include "executionController.h"

//Cons
executionController::executionController(viewer* v)
: res(0),
gData(0),
rDialogAction(0),
v(v)
{}


void executionController::setGridData(gridData* g)
{
	gData = g;
}

void executionController::setResultsDialogAction(resultsDialogAction* r)
{
	rDialogAction = r;
}



//Signal handler activated when the user has clicked the Execute algorithm button
void executionController::execute(int structure)
{
	//Check that the start and the goal cells are defined
	if(gData->startX != -1 && gData->goalX != -1)
	{
		//If it is not the first time that the user executes the algorithm
		if(res)
		{
			//If the algorithm has been previously and succesfully executed
			if(res->getCoordinates())
			{
				//We should delete the previous path found from the cell grid
				//For each cell in the previous solution
				int i = 0;
				for(std::vector<coord>::iterator it = res->getCoordinates()->begin(); it != res->getCoordinates()->end(); ++it)
				{
					//If it is a path cell
					if(*(gData->pmap + it->first + it->second*gData->width) >= Pfv::HPATH1_CELL && *(gData->pmap + it->first + it->second*gData->width) <= Pfv::RDPATH2_CELL)
					{
						//Obtain the image and the pixbuf
						Gtk::EventBox* eb = *(gData->pcells + it->first + it->second*gData->width);
				
						//Obtain the previous type of cell and store it in the cell
						switch(*(gData->initialValueCells + i))
						{
							case Pfv::ROAD_CELL:
								eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->ROAD_IMAGE);
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::ROAD_CELL;
								break;
							case Pfv::GRASS_CELL:
								eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->GRASS_IMAGE);
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::GRASS_CELL;
								break;
							case Pfv::TRAIL_CELL:
								eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->TRAIL_IMAGE);
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::TRAIL_CELL;
								break;
							case Pfv::WALL_CELL:
								eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->WALL_IMAGE);
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::WALL_CELL;
								break;
						}

					}
					i++;
				}
				//Free memory
				delete res->getCoordinates();
			}

			//Free memory
			delete gData->initialValueCells;
			delete res;
			res = 0;
		}

		//Execute the algorithm
		aStar a(gData->pmap, gData->startX, gData->startY, gData->goalX, gData->goalY);
		a.setWidth(gData->width);
		a.setHeight(gData->height);
		if(structure == UNSORTED_VECTOR) res = a.execute<unsortedVector, std::vector<Node*>::iterator>();
		else if(structure == SORTED_VECTOR_BY_F) res = a.execute<sortedVectorByF, std::vector<Node*>::iterator>();
		else if(structure == SORTED_VECTOR_BY_XY) res = a.execute<sortedVectorByXY, std::vector<Node*>::iterator>();
		else if(structure == SKIP_LIST) res = a.execute<skipList, skipListItem*>();
		else if(structure == HASH_TABLE) res = a.execute<hashTable, Node**>();
		else if(structure == HASH_TABLE_VECTOR) res = a.execute<hashTableSortedVector, Node**>();

		//If the algorithm has found a solution
		if(res->getCoordinates())
		{
			//Allocate memory to store the current value of the cells that are the path to the goal
			gData->initialValueCells = new int[res->getCoordinates()->size()];

			//Read the results and show the path solution in the grid
			int i = 0;
			for(std::vector<coord>::iterator it = res->getCoordinates()->begin(); it != res->getCoordinates()->end(); ++it)
			{
				//If it is not the goal nor the start cell
				if( !(it->first == gData->startX && it->second == gData->startY) && !(it->first == gData->goalX && it->second == gData->goalY))
				{
					//Obtain the coordinates of the previous and next path cells
					coord previous = *(it-1);
					coord next = *(it+1);
					int previousX = previous.first;
					int previousY = previous.second;
					int nextX = next.first;
					int nextY = next.second;
					int previousCell = *(gData->pmap + previousX + previousY*gData->width);

					//Obtain the image and the pixbuf
					Gtk::EventBox* eb = *(gData->pcells + it->first + it->second*gData->width);

					//Check if the path has at least one cell of path
					if(!(previousX == gData->goalX && previousY == gData->goalY && nextX == gData->startX && nextY == gData->startY))
					{
						//Check what kind of path the current cell is: horizontal, vertical and so on
						if(previousY == nextY)
						{
							//Store the initial value of the cell in case we need to restore it	
							*(gData->initialValueCells + i) = *(gData->pmap + it->first + it->second*gData->width);		

							//Depending on the situation of the trunk situation of the previous cell
							if(previousCell == Pfv::LDPATH2_CELL || previousCell == Pfv::HPATH1_CELL || previousCell == Pfv::LUPATH1_CELL || previousCell == Pfv::RUPATH1_CELL || previousCell == Pfv::RDPATH2_CELL)
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->HPATH1_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->HPATH1_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->HPATH1_TRAIL_IMAGE);
										break;
								}
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::HPATH1_CELL;
							}
							else
							{					
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->HPATH2_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->HPATH2_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->HPATH2_TRAIL_IMAGE);
										break;
								}
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::HPATH2_CELL;
							}
						}
						else if(previousX == nextX)
						{
							//Store the initial value of the cell in case we need to restore it	
							*(gData->initialValueCells + i) = *(gData->pmap + it->first + it->second*gData->width);		

							//Depending on the situation of the trunk situation of the previous cell
							if(previousCell == Pfv::LDPATH1_CELL || previousCell == Pfv::VPATH1_CELL || previousCell == Pfv::LUPATH1_CELL || previousCell == Pfv::RUPATH2_CELL || previousCell == Pfv::RDPATH2_CELL)
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->VPATH1_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->VPATH1_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->VPATH1_TRAIL_IMAGE);
										break;
								}
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::VPATH1_CELL;
							}
							else
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->VPATH2_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->VPATH2_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->VPATH2_TRAIL_IMAGE);
										break;
								}
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::VPATH2_CELL;
							}
						}
						else if((previousX > nextX && previousY > nextY && previousY > it->second) ||
								(previousX < nextX && previousY < nextY && previousX < it->first))
						{
							//Store the initial value of the cell in case we need to restore it	
							*(gData->initialValueCells + i) = *(gData->pmap + it->first + it->second*gData->width);		

							//Depending on the situation of the trunk situation of the previous cell
							if(previousCell == Pfv::VPATH1_CELL || previousCell == Pfv::HPATH2_CELL || previousCell == Pfv::LUPATH1_CELL || previousCell == Pfv::RUPATH2_CELL || previousCell == Pfv::RDPATH1_CELL)
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LDPATH1_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LDPATH1_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LDPATH1_TRAIL_IMAGE);
										break;
								}					
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::LDPATH1_CELL;
							}
							else
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LDPATH2_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LDPATH2_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LDPATH2_TRAIL_IMAGE);
										break;
								}					
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::LDPATH2_CELL;
							}
						}
						else if((previousX < nextX && previousY > nextY && previousY > it->second) ||
								(previousX > nextX && previousY < nextY && previousX > it->first))
						{
							//Store the initial value of the cell in case we need to restore it	
							*(gData->initialValueCells + i) = *(gData->pmap + it->first + it->second*gData->width);		

							//Depending on the situation of the trunk situation of the previous cell
							if(previousCell == Pfv::VPATH2_CELL || previousCell == Pfv::HPATH2_CELL || previousCell == Pfv::LUPATH2_CELL || previousCell == Pfv::RUPATH1_CELL || previousCell == Pfv::LDPATH1_CELL)
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RDPATH1_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RDPATH1_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RDPATH1_TRAIL_IMAGE);
										break;
								}
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::RDPATH1_CELL;
							}
							else
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RDPATH2_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RDPATH2_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RDPATH2_TRAIL_IMAGE);
										break;
								}
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::RDPATH2_CELL;
							}
						}
						else if((previousX > nextX && previousY > nextY && previousX > it->first) ||
								(previousX < nextX && previousY < nextY && previousY < it->second))
						{
							//Store the initial value of the cell in case we need to restore it	
							*(gData->initialValueCells + i) = *(gData->pmap + it->first + it->second*gData->width);		

							//Depending on the situation of the trunk situation of the previous cell
							if(previousCell == Pfv::LDPATH2_CELL || previousCell == Pfv::VPATH2_CELL || previousCell == Pfv::LUPATH1_CELL || previousCell == Pfv::HPATH1_CELL || previousCell == Pfv::RDPATH1_CELL)
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RUPATH1_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RUPATH1_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RUPATH1_TRAIL_IMAGE);
										break;
								}					
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::RUPATH1_CELL;
							}
							else
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RUPATH2_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RUPATH2_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->RUPATH2_TRAIL_IMAGE);
										break;
								}					
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::RUPATH2_CELL;
							}
						}
						else
						{
							//Store the initial value of the cell in case we need to restore it	
							*(gData->initialValueCells + i) = *(gData->pmap + it->first + it->second*gData->width);		

							//Depending on the situation of the trunk situation of the previous cell
							if(previousCell == Pfv::LDPATH1_CELL || previousCell == Pfv::VPATH1_CELL || previousCell == Pfv::HPATH1_CELL || previousCell == Pfv::RUPATH1_CELL || previousCell == Pfv::RDPATH2_CELL)
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LUPATH1_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LUPATH1_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LUPATH1_TRAIL_IMAGE);
										break;
								}
					
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::LUPATH1_CELL;
							}
							else
							{
								switch(*(gData->pmap + it->first + it->second*gData->width))
								{
									case Pfv::ROAD_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LUPATH2_IMAGE);
										break;
									case Pfv::GRASS_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LUPATH2_GRASS_IMAGE);
										break;
									case Pfv::TRAIL_CELL:
										//Modify the picture to show the path picture
										eb->modify_bg_pixmap(Gtk::STATE_NORMAL, gData->LUPATH2_TRAIL_IMAGE);
										break;
								}
								//Update the cell array
								*(gData->pmap + it->first + it->second*gData->width) = Pfv::LUPATH2_CELL;
							}
						}
					}
				}
				else
				{
					//Store a -1 to know that it is the start or the goal cell
					*(gData->initialValueCells + i) = -1;
				}
				i++;
			}
		}
		//If the algorithm has not found a solution
		else
		{
			//Show a info message
			Gtk::MessageDialog dialog(*v, "Impossible to find a path", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, false);
			dialog.set_secondary_text("There is no path between the start cell and the goal cell.");
			dialog.run();
		}

		//Fill the TreeView’s model with the solution data of this execution
		rDialogAction->addRow(res, gData->width*gData->height);
	}
	//If the goal or start cells are not defined
	else
	{
		//An error message is shown
		Gtk::MessageDialog dialog(*v, "Algorithm cannot be executed", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, false);
		if(gData->startX == -1 && gData->goalX == -1)
		{
			dialog.set_secondary_text("Start cell and goal cell should be defined to execute the algorithm.");
		}
		else if(gData->startX == -1)
		{
			dialog.set_secondary_text("Start cell should be defined to execute the algorithm.");
		}
		else
		{
			dialog.set_secondary_text("Goal cell should be defined to execute the algorithm.");
		}
		dialog.run();
	}
}


void executionController::freeMemory()
{
	if(res) delete res;
}